package com.hu.bgp.server;

import java.io.File;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Observable;
import java.util.Set;
import java.util.UUID;

import org.apache.commons.codec.binary.Base64;

import com.hu.bgp.exceptions.BgpServerException;
import com.hu.bgp.framework.GameStateListener;
import com.hu.bgp.framework.ServerFacade;
import com.hu.bgp.framework.ServerFacade.GameState;
import com.hu.bgp.framework.clientapi.ClientToServerMessage;

import de.novanic.eventservice.client.event.domain.DomainFactory;

/**
 * Een gametype wordt aangemaakt door de BarLoader en bevat alle informatie over
 * lopende games en hoe een nieuwe game te starten.
 * 
 * deze class word iets te vol en moet opgespilts gaan worden!
 * @author ejvos
 */
public class GameType
{
	private BarLoader barLoader;

	private Class<? extends ServerFacade> serverClass;

	private String alias;
	private String extractPath;
	private String displayName;
	private String description;

	private String barConfigFilename;

	public File sourceArchive;
	
	private GameStateListener gsListener = new GameListener();
	
	private GameInstanceCollection gameInstances = new GameInstanceCollection();

	public GameType(File source, String extractPath, String barConfigFilename)
	{
		this.extractPath = extractPath;
		this.barConfigFilename = barConfigFilename;
		barLoader = new BarLoader();
		if (barLoader.isValid(source))
		{
			this.sourceArchive = source;
			alias = source.getName();
			alias = alias.replace(".jar", "").toLowerCase();
		}
		else
		{
			throw new BgpServerException("Failed to load bar archive, invalid bar file!");
		}

		// TODO DEBUG REMOVE THIS !!!! auto activate
		//activate();
	}

	public void loaded(Class<? extends ServerFacade> serverClass, String displayName, String description)
	{
		this.serverClass = serverClass;
		this.displayName = displayName;
		this.description = description;
	}

	//TODO likely to be removed!
	public Class<?> getServerClass()
	{
		return serverClass;
	}

	public String getAlias()
	{
		return alias;
	}

	public String getFullPath()
	{

		return extractPath + "/" + alias;
	}

	public String getExtractPath()
	{
		return extractPath;
	}

	public String getDisplayName()
	{
		return displayName;
	}

	public String getDescription()
	{
		return description;
	}

	public File getSourceArchive()
	{
		return sourceArchive;
	}

	public String getBarConfigFilename()
	{
		return barConfigFilename;
	}

	public void activate()
	{
		barLoader.loadGameType(this);

	}

	public void deactivate()
	{
		// TODO
		// kill all instances
		// release resources
		// use barloader to unload class

	}

	public boolean isActive()
	{
		// TODO
		return serverClass != null;
	}

	public boolean isDeleted()
	{
		// TODO
		return false;
	}

	public void delete()
	{
		// TODO
		// check if active
		// delete resources
	}
	public Set<String> getRunningGames()
	{
		return gameInstances.getAllinstances(GameState.running);
	}
	
	//TODO No UUID 
	public ServerFacade initGameInstance()
	{
		ServerFacade game = null;
		GameContext gameContext =  new GameContext(DomainFactory.getDomain(UUID.randomUUID().toString()));
		if (serverClass == null)
		{
			try
			{
				game = (ServerFacade) serverClass.getConstructor(GameContext.class).newInstance(gameContext);
			}
			catch (Throwable e)
			{
				System.out.println("could not instantiate game");
			}
		}
		game.addGameStateListener(gsListener);
		game.setGUID(createGameID(game));
		gameInstances.put(game,game.getGameState());
		return game;
	}
	/*
	 * ID is base64 encoded  hashcode (which is a final method in the Server class)
	 * the '=' placeholders for values that are not multiple of 4 are removed
	 */
	private String createGameID(ServerFacade game)
	{
		return new String(Base64.encodeBase64(intToByteArray(game.hashCode()))).replace('=', ' ').trim();
	}
	public static byte[] intToByteArray(int value) {
        byte[] b = new byte[4];
        for (int i = 0; i < 4; i++) {
            int offset = (b.length - 1 - i) * 8;
            b[i] = (byte) ((value >>> offset) & 0xFF);
        }
        return b;
    }
	
	class GameListener implements GameStateListener{

		public void update(ServerFacade gameServer, GameState state) {
			gameInstances.changeState(gameServer.getGUID(), state);
		}

		public void update(Observable o, Object arg) {
			if(o instanceof ServerFacade && arg instanceof GameState)
				update(o, arg);
			
		}
		
	}
}
